import re

from Ft.Server import FTSS_URI_SCHEME
from Ft.Server.Common.ResourceTypes import ResourceType
from Ft.Rdf import OBJECT_TYPE_LITERAL

from Ft.Rdf.Terms import Regexic

from myelin.Ns import MYELIN

FTSS_URI_ROOT = '%s:///' % FTSS_URI_SCHEME
FTSS_URI_ROOT_LEN = len(FTSS_URI_ROOT)

del FTSS_URI_SCHEME



class AgentAcls:    
    
    ACL_PAT       = "(.*)\|.*\|.* (?:%s) .*|(.*)\|.* (?:%s) .*\|.*"
    VALID_ACL_PAT = "(?:.*)\|.*\|.* (?:.*) .*|(?:.*)\|.* (?:.*) .*\|.*"
        
    isValidAcl = re.compile(VALID_ACL_PAT).match
    predicateAcl = MYELIN + 'acl'
    aclsExt = 'acls'
    
    #aclsModelName = 'acls' # FIXME: add to server
    
    def __init__(self, driver, isAdmin=False):        
        self._dbDriver = driver
        self._isAdmin = isAdmin
        
    def getQueryCriteria(self, uri, cache):
        uri = uri[0] == '/' and uri[1:].split('/') or \
            uri[FTSS_URI_ROOT_LEN:].split('/')
        
        s = FTSS_URI_ROOT
        r = [s].append
        for p in uri[:-1]:
            s += p + '/'
            r(s)
        if uri[-1]:
            r(s+uri[-1])
            i = 0
        else:
            i = 1
                
        r = r.__self__
        r.reverse()
        c = cache.get
        for p in r:
            a = c(p)
            if a is not None:
                a = a.copy()            
                uri.reverse()
                uri = uri[:i]
                uri.reverse()            
                r = r[:i]
                break
            i += 1
        else:
            p = FTSS_URI_ROOT
            a = set()
            
        for i in uri[:-1]:
            p += '(\.|' + i + '/'
        if uri[-1]:
            p += '(\.|' + uri[-1] + '\.' + ')'*len(uri) + self.aclsExt
        else:
            p += '\.' + ')'*(len(uri)-1) + self.aclsExt
            
        r.reverse()
        return r, p, a


    
    def resetBase(self, uri, ids):
                
        #ids = '|'.join(ids)
        pat = re.compile(self.ACL_PAT % (ids,ids)).match
        cache = {}
                
        uriOrder, scopePat, a = self.getQueryCriteria(uri, cache)
        a_copy = a.copy
        
        #print "SP: "
        #print " ", uriOrder
        #print " ", scopePat
        #print " ", a
        
        acls = {}
        acls_op = acls.setdefault
                
        #driverS = self._dbDriver.subjectsFromPredAndObjs
        l = []
        for s in self._dbDriver.complete(
            None,
            None,
            None,
            None,
            Regexic(scopePat)
        ):
            #print "ACLS COMPLETE: ", s
            acls_op(s[4],l).append(s[2])
            if l:
                l = []
        
        acls_op = acls.get
        ext = self.aclsExt
        for id in uriOrder:
            #if __debug__: print id
            for s in acls_op('%s.%s'%(id,ext),l):
                s = pat(s)
                if s:
                    s = s.groups()
                    if s[0]:
                        a.discard(s[0])
                    elif s[1]:
                        a.add(s[1])
            cache[id] = a_copy()
            
        self.base = a
        self.uri = uri
        self._permPat = pat
        self._aclCache = cache
        
    def __contains__(self, action):
        return action in self.base or self._isAdmin
        
    def isPermittedAction(self, action):
        return action in self.base or self._isAdmin
        
    #def verify(self, action, method):
    #    assert hasattr(self, 'base')
    #    # add check for method as well
    #    #  ie. action:method in base ... 'view:html' or 'view' in base
    #    return action in self.base
    #def createKey(self, action, method):
    #    return action + ':' + method

    def getChildAcls(self, id):
        # only use this function if you know for sure the id is a contained child
        assert hasattr(self, 'base')
        pat = self._permPat
        a = self.base.copy()
        for s in self._dbDriver.objectsFromSubsAndPred(None,None,id + self.aclsExt):
            s = pat(s[0])
            if s:
                s = s.groups()
                if s[0]:
                    a.discard(s[0])
                elif s[1]:
                    a.add(s[1])
        self._aclCache[id] = a
        return a
    
    def createChildAcls(self, id):
        assert hasattr(self, 'base')
        
        aclObj = AgentAcls(self._dbDriver, self._isAdmin)
        
        aclObj.base = self.getChildAcls(id)
        aclObj.uri = id
        aclObj._permPat = self._permPat
        aclObj._aclCache = self._aclCache
        
        return aclObj
    
    def getNodeAcls(self, uri):
        assert hasattr(self, 'base')
        
        pat = self._permPat
        cache = self._aclCache
        
        # this initial test will fail for most use-cases
        a = cache.get(uri)
        if a is not None:
            return a
        
        uriOrder, scopePat, a = self.getQueryCriteria(uri, cache)
        a_copy = a.copy
        
        #print "SP: "
        #print " ", uriOrder
        #print " ", scopePat
        #print " ", a
        
        acls = {}
        acls_op = acls.setdefault
                
        #driverS = self._dbDriver.subjectsFromPredAndObjs
        l = []
        for s in self._dbDriver.complete(
            None,
            None,
            None,
            None,
            Regexic(scopePat)
        ):
            #print "ACLS COMPLETE: ", s
            acls_op(s[4],l).append(s[2])
            if l:
                l = []
        
        acls_op = acls.get
        ext = self.aclsExt
        for id in uriOrder:
            #if __debug__: print id
            for s in acls_op('%s.%s'%(id,ext),l):
                s = pat(s)
                if s:
                    s = s.groups()
                    if s[0]:
                        a.discard(s[0])
                    elif s[1]:
                        a.add(s[1])
            cache[id] = a_copy()
        
        return a    
    
    def createNodeAcls(self, id):
        assert hasattr(self, 'base')
        
        aclObj = AgentAcls(self._dbDriver, self._isAdmin)
        
        aclObj.base = self.getNodeAcls(id)
        aclObj.uri = id
        
        aclObj._permPat = self._permPat
        aclObj._aclCache = self._aclCache
        
        return aclObj
    
    def checkNodeAcls(self, id, action='view'):
        return self._isAdmin or action in self.getNodeAcls(id)
        
    def checkChildAcls(self, id, action):
        return self._isAdmin or action in self.childNodeAcls(id)
    
    def removeAcl(self, acl):
        # caller must check permissions
        scope = self.uri + self.aclsExt
        self._dbDriver.rdf_owner.verifyWriteAcl(scope)
        
        if not self.isValidAcl(acl):
            return
                   
        self._dbDriver.removePattern(
            Regexic('.*'),
            Regexic('.*'),
            Regexic(acl.replace('|','\|')),
            Regexic('.*'),
            scope
        )
        
        return True
    
    def removeAclRegex(self, acl):
        # caller must check permissions
        scope = self.uri + self.aclsExt
        self._dbDriver.rdf_owner.verifyWriteAcl(scope)
        
        if not self.isValidAcl(acl):
            return
                  
        self._dbDriver.removePattern(
            Regexic('.*'),
            Regexic('.*'),
            Regexic(acl.replace('|','\|')),
            Regexic('.*'),
            scope
        )
        return True
    
    def setAcl(self, acl):
        # caller must check permissions
        scope = self.uri + self.aclsExt
        self._dbDriver.rdf_owner.verifyWriteAcl(scope)
        
        if not self.isValidAcl(acl):
            return
        
        self._dbDriver.removePattern(
            Regexic('.*'),
            Regexic('.*'),
            Regexic('.*'),
            Regexic('.*'),
            scope
        )
        
        self._dbDriver.add(((
            self.uri,
            self.predicateAcl,
            acl,
            None,
            scope,
            OBJECT_TYPE_LITERAL
        ),))
        return True
        
    def addAcl(self, acl):
        # verify acl
        scope = self.uri + self.aclsExt
        self._dbDriver.verifyWriteAcl(scope)
        
        if not self.isValidAcl(acl):
            return
        
        self._dbDriver.add(((
            self.uri,
            self.predicateAcl,
            acl,
            None,
            scope,
            OBJECT_TYPE_LITERAL
        ),))
        return True
    